pub mod timer;

use core::{ffi::c_void, marker::PhantomData};

use alloc::rc::Rc;
use uefi_ffi::{
    services::boot::{
        event::{
            EfiEvent, EFI_EVENT_GROUP_AFTER_READY_TO_BOOT,
            EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES, EFI_EVENT_GROUP_EXIT_BOOT_SERVICES,
            EFI_EVENT_GROUP_MEMORY_MAP_CHANGE, EFI_EVENT_GROUP_READY_TO_BOOT,
            EFI_EVENT_GROUP_RESET_SYSTEM, EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE,
            EVT_NOTIFY_SIGNAL_MASK, EVT_NOTIFY_WAIT_MASK, EVT_RUNTIME_MASK,
            EVT_SIGNAL_EXIT_BOOT_SERVICES, EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE_MASK, EVT_TIMER_MASK,
        },
        EfiGuid,
    },
    EfiStatus,
};

use crate::{task::Tpl, BootServices};

pub trait EventTarget: Sized {
    /// 内部实现需要创建 _Event 的实例，并且设置处理程序
    /// self.on::<TimeOutEvent>(|x| {});
    type Callback;
    fn dispatch_event(&self, event: EventId);
    fn on<ET: DefaultEventHandler>(&mut self, _cb: Self::Callback);
}

/// EventTarget 需要实现本函数
pub trait DefaultEventHandler: Sized {
    // 内部一般调用 Target::dispatch_event
    extern "efiapi" fn default_handler(_raw_event: EfiEvent, _target: *mut c_void) {}
}
impl<T> DefaultEventHandler for Rc<T>
where
    Self: EventTarget,
{
    extern "efiapi" fn default_handler(_raw_event: EfiEvent, _target: *mut c_void) {
        let rc_self: Self = unsafe { Rc::from_raw(_target.cast()) };
        rc_self.dispatch_event(unsafe { EventId::from_raw(_raw_event) });
    }
}
/// Composite 类型的事件不能属于某一个组。
pub struct CompositeEvent;
/// 常规的事件类型，如：
///
/// + RUNTIME
/// + NOTIFY_WAIT
/// + NOTIFY_SIGNAL
pub struct CommonEvent;
/// 定时事件，可以和常规的事件类型组合。
pub struct EventBaseTimer;
impl EventBaseType for CompositeEvent {}
impl EventBaseType for CommonEvent {}
impl EventBaseType for EventBaseTimer {}
pub struct GroupId {
    guid: EfiGuid,
}
impl GroupId {
    pub const fn new(guid: EfiGuid) -> Self {
        Self { guid }
    }
    pub const fn guid(&self) -> &EfiGuid {
        &self.guid
    }

    pub const EXIT_BOOT_SERVICES: Self = Self::new(EFI_EVENT_GROUP_EXIT_BOOT_SERVICES);
    pub const BEFORE_EXIT_BOOT_SERVICES: Self =
        Self::new(EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES);
    pub const VIRTUAL_ADDRESS_CHANGE: Self = Self::new(EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE);
    pub const MEMORY_MAP_CHANGE: Self = Self::new(EFI_EVENT_GROUP_MEMORY_MAP_CHANGE);
    pub const READY_TO_BOOT: Self = Self::new(EFI_EVENT_GROUP_READY_TO_BOOT);
    pub const AFTER_READY_TO_BOOT: Self = Self::new(EFI_EVENT_GROUP_AFTER_READY_TO_BOOT);
    pub const RESET_SYSTEM: Self = Self::new(EFI_EVENT_GROUP_RESET_SYSTEM);
}

pub trait EventBaseType {}
pub struct EventType<T: EventBaseType> {
    code: u32,
    phantom: PhantomData<T>,
}
impl<T: EventBaseType> EventType<T> {
    pub fn raw(&self) -> u32 {
        self.code
    }
}
impl EventType<CompositeEvent> {
    pub const fn signal_after_exit() -> Self {
        Self {
            code: EVT_SIGNAL_EXIT_BOOT_SERVICES,
            phantom: PhantomData,
        }
    }

    pub const fn signal_after_vmap() -> Self {
        Self {
            code: EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE_MASK,
            phantom: PhantomData,
        }
    }
}
impl EventType<CommonEvent> {
    pub const fn config() -> Self {
        Self {
            code: 0,
            phantom: PhantomData,
        }
    }
    pub const fn runtime(mut self, flag: bool) -> Self {
        if flag {
            self.code |= EVT_RUNTIME_MASK;
        } else {
            self.code ^= EVT_RUNTIME_MASK;
        }
        self
    }
    pub const fn notify_wait(mut self, flag: bool) -> Self {
        if flag {
            self.code |= EVT_NOTIFY_WAIT_MASK;
        } else {
            self.code ^= EVT_NOTIFY_WAIT_MASK;
        }
        self
    }
    pub const fn notify_queued(mut self, flag: bool) -> Self {
        if flag {
            self.code |= EVT_NOTIFY_SIGNAL_MASK;
        } else {
            self.code ^= EVT_NOTIFY_SIGNAL_MASK;
        }
        self
    }
    pub const fn timer(mut self, flag: bool) -> EventType<EventBaseTimer> {
        if flag {
            self.code |= EVT_TIMER_MASK;
        } else {
            self.code ^= EVT_TIMER_MASK;
        }
        EventType::<EventBaseTimer> {
            code: self.code,
            phantom: PhantomData,
        }
    }
}
impl EventType<EventBaseTimer> {
    pub const fn config() -> Self {
        Self {
            code: EVT_TIMER_MASK,
            phantom: PhantomData,
        }
    }

    pub const fn runtime(mut self, flag: bool) -> Self {
        if flag {
            self.code |= EVT_RUNTIME_MASK;
        } else {
            self.code ^= EVT_RUNTIME_MASK;
        }
        self
    }
    pub const fn notify_wait(mut self, flag: bool) -> Self {
        if flag {
            self.code |= EVT_NOTIFY_WAIT_MASK;
        } else {
            self.code ^= EVT_NOTIFY_WAIT_MASK;
        }
        self
    }
    pub const fn notify_queued(mut self, flag: bool) -> Self {
        if flag {
            self.code |= EVT_NOTIFY_SIGNAL_MASK;
        } else {
            self.code ^= EVT_NOTIFY_SIGNAL_MASK;
        }
        self
    }
}
/**
 * 1. notify_fn 的参数类型过于宽泛，并不希望将其暴露给用户，但该函数签字有需要传递给内部实现。
 * 2. 无法使用 closure 代替 notify_fn，因为 closure 无法确保其遵循的 abi。
 *
 */
pub trait EventConfig<T: EventBaseType>: Sized {
    const EVENT_TYPE: EventType<T>;
    const NOTIFY_TPL: Tpl;
}
#[repr(transparent)]
#[derive(PartialEq, PartialOrd)]
pub struct EventId {
    raw: EfiEvent,
}
impl EventId {
    pub unsafe fn from_raw(raw: EfiEvent) -> Self {
        Self { raw }
    }
    /// 我们可以安全的获取原始事件 id，但是任何读写操作都是不安全的。
    pub fn raw(&self) -> EfiEvent {
        self.raw
    }
    pub fn close(self) {
        BootServices::take().close_event(self.raw);
    }
    /// 触发信号。
    ///
    /// 本函数对于那些已经触发的事件不起作用。
    ///
    /// 根据事件类型，事件的通知函数会有不同的表现：
    /// + 若事件类型为 EVT_NOTIFY_SIGNAL，则事件的通知函数会按照事件的优先级处于调度状态。
    /// > signal 函数本身可以从任何任务优先级调用。
    ///
    /// + 如果事件从属于某个事件组，那么事件组中的所有事件都会被触发，并且他们的通知函数都会被调度。
    ///
    /// 事件组在触发的时候，可以关闭事件组中的某个事件，或者创建一个归属于改组的事件。
    pub fn signal(&mut self) {
        BootServices::take().signal_event(self.raw);
    }
    /// 检查事件是否已被触发
    ///
    /// 如果事件类型为 EVT_NOTIFY_SIGNAL，则永远返回 false。
    ///
    /// ⚠️ 如果事件未处于被触发状态，但是有通知函数。则通知函数会按照相应的任务优先级进行排队。
    /// 如果通知函数的执行会导致事件被触发。，则触发状态被清除并且返回 true。
    pub fn is_signaled(&self) -> bool {
        if BootServices::take().check_event(self.raw).is_sucess() {
            true
        } else {
            false
        }
    }
    // pub fn new_with<Target: EventTarget, T: Event<Target>, Ctx>(
    //     context: NonNull<Ctx>,
    // ) -> Result<Self, EfiStatus> {
    //     let mut event = NonNull::dangling();
    //     let status = BootServices::take().create_event_ex(
    //         T::EVENT_TYPE.raw(),
    //         T::DEFAULT_NOTIFY_TPL as usize,
    //         T::default_handler,
    //         context.as_ptr().cast(),
    //         T::GROUP.map_or(null(), |x| x.guid() as *const EfiGuid),
    //         &mut event,
    //     );
    //     if status.is_sucess() {
    //         return Ok(Self { raw: event });
    //     } else {
    //         return Err(status);
    //     }
    // }
    // pub fn new<Target: EventTarget, T: Event<Target>>() -> Result<Self, EfiStatus> {
    //     let mut event = NonNull::dangling();
    //     let status = BootServices::take().create_event_ex(
    //         T::EVENT_TYPE.raw(),
    //         T::DEFAULT_NOTIFY_TPL as usize,
    //         T::default_handler,
    //         null_mut(),
    //         T::GROUP.map_or(null(), |x| x.guid() as *const EfiGuid),
    //         &mut event,
    //     );
    //     if status.is_sucess() {
    //         return Ok(Self { raw: event });
    //     } else {
    //         return Err(status);
    //     }
    // }
}

pub trait EventArrayWait {
    fn wait(&self) -> Result<usize, EfiStatus>;
}
impl EventArrayWait for [EventId] {
    fn wait(&self) -> Result<usize, EfiStatus> {
        let mut index = 0;
        let result = BootServices::take().wait_for_event(
            self.len(),
            self.as_ptr().cast(),
            &mut index as *mut usize,
        );
        if result.is_sucess() {
            return Ok(index);
        } else {
            return Err(result);
        }
    }
}

#[cfg(test)]
mod example {

    use super::*;
    use alloc::vec::Vec;
    use uefi_ffi::services::boot::event::EfiTimerDelay::TimerRelative;

    pub struct Data {
        // 如果 Data 所能监听的事件固定，也可以不用动态存储。动态存储也可以考虑 hash 表。
        event: Vec<(EventId, fn(context: Rc<Data>))>,
    }
    impl EventTarget for Rc<Data> {
        type Context = Self;
        fn dispatch_event(&self, event: EventId) {
            let pair = self.event.iter().find(|x| x.0 == event);
            match pair {
                Some(x) => x.1(Rc::clone(&self)),
                None => todo!(),
            }
        }

        fn on<TypeBase, ET: DefaultEventHandler<TypeBase, Self>>(
            &mut self,
            cb: fn(arg: Self::Context),
        ) {
            let mut result = ptr::null_mut();
            BootServices::take().create_event_ex(
                ET::EVENT_TYPE.code,
                ET::DEFAULT_NOTIFY_TPL as usize,
                ET::default_handler,
                self as *mut Self as *mut c_void,
                ptr::null(),
                &mut result,
            );
            Rc::get_mut(self)
                .unwrap()
                .event
                .push((EventId { raw: result }, cb));
        }
    }
    pub struct ExampleEvent {}
    impl ExampleEvent {}
    impl<Target: EventTarget> DefaultEventHandler<EventBaseTimer, Target> for ExampleEvent {
        const EVENT_TYPE: EventType<EventBaseTimer> = EventType::<EventBaseTimer>::config();

        const DEFAULT_NOTIFY_TPL: Tpl = Tpl::Notify;

        const GROUP: Option<GroupId> = None;

        const EVENT_NAME: &'static str = "Default_Event";
    }
    #[test]
    pub fn test() {
        fn do_something(data: Option<Rc<u64>>) {
            if let Some(mut x) = data {
                *Rc::get_mut(&mut x).unwrap() += 1;
            }
        }
        Timeout::<u64>::doit(do_something).with_init(64).after(100);
    }

    pub struct Timeout<T> {
        data: Option<Rc<T>>,
        handler: fn(Option<Rc<T>>),
        eventid: EventId,
    }
    impl<T> Timeout<T> {
        pub fn after(&mut self, time: u64) {
            BootServices::take().set_timer(self.eventid.raw, TimerRelative, time);
        }
        pub fn with_init(&mut self, data: T) -> &mut Self {
            self.data = Some(Rc::new(data));
            self
        }
        pub fn doit(cb: fn(Option<Rc<T>>)) -> Self {
            let mut ret = Self {
                data: None,
                handler: cb,
                eventid: EventId {
                    raw: ptr::null_mut(),
                },
            };
            ret.on::<Self>(cb);
            ret
        }
    }
    impl<T> EventTarget for Timeout<T> {
        type Context = Option<Rc<T>>;
        fn dispatch_event(&self, _event: EventId) {
            match &self.data {
                Some(x) => (self.handler)(Some(Rc::clone(&x))),
                None => (self.handler)(None),
            }
        }

        fn on<TypeBase, ET: DefaultEventHandler<TypeBase, Self>>(
            &mut self,
            cb: fn(arg: Option<Rc<T>>),
        ) {
            let mut result = ptr::null_mut();
            let ctx = match self.data {
                Some(ref mut x) => Rc::get_mut(x).unwrap() as *mut T as *mut c_void,
                None => ptr::null_mut(),
            };
            BootServices::take().create_event_ex(
                ET::EVENT_TYPE.code,
                ET::DEFAULT_NOTIFY_TPL as usize,
                ET::default_handler,
                ctx,
                ptr::null(),
                &mut result,
            );
            self.handler = cb;
            self.eventid = EventId { raw: result };
        }
    }
    impl<T, Target: EventTarget> DefaultEventHandler<EventBaseTimer, Target> for Timeout<T> {
        const EVENT_TYPE: EventType<EventBaseTimer> = EventType::<EventBaseTimer>::config();

        const DEFAULT_NOTIFY_TPL: Tpl = Tpl::Notify;

        const GROUP: Option<GroupId> = None;

        const EVENT_NAME: &'static str = "Default_Event";

        extern "efiapi" fn default_handler(event: EfiEvent, target: *mut c_void) {
            let _target = unsafe { Rc::<Target>::from_raw(target as *const Target) };
            _target.dispatch_event(EventId { raw: event });
            unsafe {
                Rc::decrement_strong_count(target);
            }
        }
    }
}
